Imports

Global Variables

popSize <- 500
nChr <- 10
nSegSites <- 100
nGens <- 50

Functions

oneTraitFitFunc <- function(x) {
  return (-(x)^2) + rnorm(1,sd=2)
}

twoTraitFitFunc <- function(x) {
  res = -(x[,1]^2) - x[,2]^2
  return (res)
}

calculateFitnessTwoTrait <- function(x,y) {
  return ((-(x)^2)+(-(y)^2))
}

hetLocus <- function(locus) {
  return (length(unique(locus)) > 1)
}

Plotting Functions

theme <- theme(plot.background = ggplot2::element_blank(),
               panel.background = ggplot2::element_blank(),
               axis.line = ggplot2::element_line(linewidth = 0.2),
               plot.title = ggplot2::element_text(hjust = 0.5,
                                                  face = 'bold',
                                                  size = 12),
               axis.text = ggplot2::element_text(size  = 12,
                                                 color = 'black'),
               axis.title = ggplot2::element_text(size  = 12,
                                                  face = 'bold',
                                                  color = 'black'))

# Expects a dataframe with 2 columns:
# traitValA, traitValB (for type "CONTOUR"),
# with a 3rd column (fitness) (for type "SURFACE")
overlayWalkOnLandscape <-function(df,
                                  type="CONTOUR",
                                  fitCalc,
                                  traitAMin=-10,
                                  traitAMax=10,
                                  traitBMin=-10,
                                  traitBMax=10) {

  fitness_x = seq(traitAMin,traitAMax, by=0.25)
  fitness_y = seq(traitBMin,traitBMax, by=0.25)
  fitness_z = outer(fitness_x,fitness_y,fitCalc)
  fig <- plot_ly()

  if (type == "CONTOUR") {
    plot_ly() %>%
      layout(xaxis = list(title = "Trait A", constrain = "domain"),
                          yaxis = list(title = "Trait B", scaleanchor="x")) %>%
      add_trace(
        fig,
        x=fitness_x,
        y=fitness_y,
        z=fitness_z,
        type='contour',
        colorscale = "RdBu",
        colorbar=list(title = "w"),
        line = list(color = 'black', width = 1),
        opacity=1) %>%
      add_trace(
        fig,
        df,
        name = popSize,
        x = df$traitValA,
        y = df$traitValB,
        type='scatter',
        mode = 'lines',
        line = list(color = 'black', width = 4, dash = 'solid'),
        opacity = 1)
  } else if (type == "SURFACE"){
    plot_ly() %>%
      layout(scene = list(xaxis = list(title = "Trait A"),
                          yaxis = list(title = "Trait B"),
                          zaxis = list(title = "w"),
                          aspectmode='cube')) %>%
      add_trace(
        fig,
        df,
        name = popSize,
        x = df$traitValA,
        y = df$traitValB,
        z = df$fitness,
        type = 'scatter3d',
        mode = 'lines',
        opacity = 1,
        line = list(width = 10)
      ) %>%
      add_trace(
        fig,
        x=fitness_x,
        y=fitness_y,
        z=fitness_z,
        type='surface',
        colorbar=list(title = "w"),
        colors = "PuBuGn",
        opacity=0.9)
      
  } else {
    print("Type Not Supported")
  }
}

# Plot a population on a a 3d fitness chart
# only works for populations with 2 traits
# TODO: figure out how to add colorbar label
plot3dPopulationFitness <- function(pop, fitCalc) {
  popSize <- nInd(pop)
  df <- data.frame(traitA=numeric(popSize),
                   traitB=numeric(popSize),
                   fitness=numeric(popSize))
  for (i in 1:popSize) {
    df$traitA[i] <- pheno(pop)[i,1]
    df$traitB[i] <- pheno(pop)[i,2]
    df$fitness[i] <- fitCalc(df$traitA[i], df$traitB[i])
  }
  fig <- plot_ly()
  
  plot_ly() %>% 
    layout(scene = list(xaxis = list(title = "Trait 1"),
                        yaxis = list(title = "Trait 2"),
                        zaxis = list(title = "w"),
                        aspectmode='cube')) %>%
    add_trace(
      fig,
      df,
      name = popSize,
      x = df$traitA,
      y = df$traitB,
      z = df$fitness,
      type = 'scatter3d',
      mode = 'markers',
      color=df$fitness)
}

# Plots the trait architecture on a genetic basis
# Works for 1 or 2 trait populations
plotTraitArchitecture <- function(pop, fitFunc) {
  fit_pre <- mean(fitFunc(gv(pop)))
  geno <- pullSegSiteGeno(pop)
  cols <- colnames(geno)
  nLoci <- length(cols)
  popSize <- nrow(geno)
  eff_sizes <- data.frame(id = character(nLoci),
                          eff_size=numeric(nLoci))
  for (l in 1:nLoci) {
    id <- cols[l]
    eff_sizes$id[l] <- id
    locus = geno[,l]
    if (!hetLocus(locus)) {
      eff_sizes$eff_size[l] <- NA
      next
    }
    strs = unlist(strsplit(id, "_"))
    chr = strtoi(strs[1])
    site = strtoi(strs[2])
    pop1 <- editGenome(pop, ind=c(1:popSize), chr=chr, segSites=site, allele=0, simParam=SP)
    pop2 <- editGenome(pop, ind=c(1:popSize), chr=chr, segSites=site, allele=1, simParam=SP)
    effect_size_1 <- mean(fitFunc(gv(pop1))) - fit_pre
    effect_size_2 <- mean(fitFunc(gv(pop2))) - fit_pre
    if (effect_size_1 > 0) {
      eff_sizes$eff_size[l] <- effect_size_1
    } else if (effect_size_2 > 1) {
      eff_sizes$eff_size[l] <- effect_size_2
    } else {
      eff_sizes$eff_size[l] <- 0
    }
  }
  eff_sizes <- eff_sizes[apply(eff_sizes!=0, 1, all),]
  eff_sizes <- na.omit(eff_sizes)
  ggplot(data=eff_sizes, aes(x=reorder(id, -eff_size), y=eff_size)) +
    geom_bar(stat="identity") +
    labs(x = "Variant Id", y = "Effect Size") +
    theme +
    theme(axis.text.x = element_text(angle = 45, vjust = 0.5, hjust=1, size = 6, margin = margin(b = 10)),
          axis.text.y = element_text(margin = margin(l=10, r=10)))
}

# Show the fitness of a population
plot_fitness <- function(df) {
  ggplot(data=df, aes(x=gen, y=fitness)) +
    geom_line() +
    geom_point() +
    labs(x = "Generation", y = "Fitness")
}

# Plot Genetic Values for Two Traits
plot_hist <- function(pop) {
  gv_a = gv(pop)[,1]
  gv_b = gv(pop)[,2]
  idx = c(1:length(gv_a))
  df <- data.frame(gv_a, gv_b)
  df <- melt(as.data.table(df))
  ggplot(df, aes(x=value, color=variable)) + geom_histogram(binwidth=1, position='identity')
}

Play around with different fitness functions


x = seq(-100,100, by=1)
y = oneTraitFitFunc(x)

df <- data.frame(x=x, y=y)
ggplot(data=df, aes(x=x, y=y)) +
    geom_line() +
    geom_point() +
    labs(x = "Trait Value", y = "Fitness")

fitness_x = seq(-10,10, by=0.25)
fitness_y = seq(-10,10, by=0.25)
fitness_z = outer(fitness_x,fitness_y,calculateFitnessTwoTrait)


plot_ly(x=fitness_x,
        y=fitness_y,
        z=fitness_z,
        type='surface',
        colors = "PuBuGn",
        opacity=1) %>%
  layout(scene = list(xaxis = list(title = "Trait A"),
                      yaxis = list(title = "Trait B"),
                      zaxis = list(title = "w"),
                      aspectmode='cube'))

# Valid arguments:
# colors = "PuBuGn"
# colors = colorRampPalette(c("blue", "orange"))(15)
# colors = magma(50, alpha = 1, begin = 0, end = 1, direction = 1) (viridis, plasma, magma, inferno)

Show that all individuals converge to a fitness maximum

start_trait_val <- c(-50,-25,-10,10,25,50)
numSims <- length(start_trait_val)
popSize <- 500
nSegSites <- 50

df <- data.frame(gen=rep(1:nGens, times=numSims),
                 fitness=numeric(nGens*numSims),
                 initialVal = numeric(nGens*numSims),
                 traitVal=numeric(nGens*numSims))

for (s in 1:numSims) {
  initVal <- start_trait_val[s]
  founders = quickHaplo(
     nInd=popSize,
     nChr=nChr,
     segSites=nSegSites
  )
  SP <- SimParam$new(founders)
  SP$addTraitA(
    10,
    mean=initVal,
    var=abs(initVal/2)
  )
  SP$setVarE(H2=0.5)
  pop <- newPop(founders, simParam=SP)
  
  for(gen in 1:nGens) {
    idx = (s-1)*nGens + gen
    df$fitness[idx] <- oneTraitFitFunc(pheno(pop))
    df$traitVal[idx] <- meanP(pop)
    df$initialVal[idx] <- initVal
    pop <- selectCross(pop=pop, trait=oneTraitFitFunc, nInd=popSize/2, nCrosses=popSize)
  }
}
Warning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement lengthWarning: number of items to replace is not a multiple of replacement length
df$initialVal = as.character(df$initialVal)
ggplot(data=df, aes(x=gen, y=traitVal)) +
  geom_line(aes(color = initialVal)) +
  labs(x = "Generation", y = "Trait Value")

Plot Trait Value against Fitness

nGens <- 50
nInd <- 500
nSegSites <- 1000


founders = quickHaplo(
   nInd=nInd,
   nChr=nChr,
   segSites=nSegSites
)
SP <- SimParam$new(founders)
SP$addTraitA(
  10,
  mean=50,
  var=abs(initVal/2)
)
SP$setVarE(H2=0.5)
pop <- newPop(founders, simParam=SP)

fit_df <- data.frame(gen=1:nGens,
                 fitness=numeric(nGens),
                 traitVal=numeric(nGens))
for(gen in 1:nGens) {
  fit_df$fitness[gen] <- fitnessFunc(meanP(pop))
  fit_df$traitVal[gen] <- meanP(pop)
  pop <- selectCross(pop=pop, trait=oneTraitFitFunc, nInd=popSize/2, nCrosses=popSize)
}

par(mar = c(5, 5, 3, 5) + 0.3)
plot(fit_df$gen, fit_df$traitVal, type="l", lwd = "3", col=2, xlab = "Generation", ylab = "Trait Value")   
par(new = TRUE) 
plot(fit_df$gen, fit_df$fitness, type="l", lwd = "3", col = 3, axes = FALSE, xlab = "", ylab = "") 
axis(side = 4, at = pretty(range(fit_df$fitness)))
mtext("Fitness", side = 4, line = 3)
par(xpd=TRUE)
legend("right",
  c("Trait Value", "Fitness"),
       lty = 1,
       lwd = 3,
       col = 2:3)

Create 1 Trait Population

popSize = 200
nSegSites = 50
nLoci = nSegSites * nChr

founders = runMacs(
   nInd=popSize,
   nChr=nChr,
   segSites=nSegSites
)
SP <- SimParam$new(founders)
SP$addTraitA(
  mean=20,
  var=5,
  nQtlPerChr = 10 # changing this changes effect size? 100 QTL
)

SP$setVarE(H2=0.5)
foundingPop <- newPop(founders, simParam = SP)
plotTraitArchitecture(foundingPop, oneTraitFitFunc)

Create 2 Trait Population

set.seed(123)
popSize = 1000
nSegSites = 100

founders = runMacs(
   nInd=popSize,
   nChr=nChr,
   segSites=nSegSites
)
SP <- SimParam$new(founders)
SP$addTraitA(mean=c(runif(1,-10,10),runif(1,-10,10)), var=c(1,1), nQtlPerChr=10)

SP$setVarE(H2=c(0.5,0.5))
foundingPop <- newPop(founders, simParam = SP)

#plot3dPopulationFitness(foundingPop, calculateFitnessTwoTrait)
#plotTraitArchitecture(foundingPop, twoTraitFitFunc)

Out of alleles left in the population, how many of them would increase fitness? Next: how does this change with size of allele? Look at editGenomeTopQtl() Next: how does size of ‘fitness delta’ change over generations?

nGens = 20
pop <- foundingPop
selIntensity <- 0.2

cols = colnames(pullSegSiteGeno(pop))
df <- data.frame(gen=rep(1:nGens),
                 fitness=numeric(nGens),
                 traitVal=numeric(nGens),
                 percFitInc=numeric(nGens),
                 segAlleles=numeric(nGens))

for (gen in 1:nGens) {
  print(gen)
  df$fitness[gen] <- fitnessFunc(meanG(pop))
  df$traitVal[gen] <- meanG(pop)
  num_het_loci = 0
  num_inc = 0
  geno <- pullSegSiteGeno(pop)
  for (l in 1:nLoci) {
    strs = unlist(strsplit(cols[l], "_"))
    chr = strtoi(strs[1])
    site = strtoi(strs[2])
    locus = geno[,l]
    inc <- FALSE
    if (hetLocus(locus)) {
      num_het_loci <- num_het_loci + 1
      fit_pre <- fitnessFunc(meanG(pop))
      pop0 <- editGenome(pop, ind=c(1:popSize), chr=chr, segSites=site, allele=0, simParam=SP)
      pop1 <- editGenome(pop, ind=c(1:popSize), chr=chr, segSites=site, allele=1, simParam=SP)
      fit_post0 <- fitnessFunc(meanG(pop0))
      fit_post1 <- fitnessFunc(meanG(pop1))
      if ((fit_post0 > fit_pre) || (fit_post1 > fit_pre) ) {
        inc <- TRUE
      }
    }
    if (inc == TRUE) {
      num_inc <- num_inc + 1
    }
    if (FALSE) {
      for (i in 1:nInd(pop)) {
        ind = geno[i,]
        fit_pre <- fitnessFunc(pop@gv[i])
        pop0 <- editGenome(pop, ind=i, chr=chr, segSites=site, allele=0, simParam=SP)
        pop1 <- editGenome(pop, ind=i, chr=chr, segSites=site, allele=1, simParam=SP)
        
        fit_post0 <- fitnessFunc(pop0@gv[i])
        fit_post1 <- fitnessFunc(pop1@gv[i])
        if ((fit_post0 > fit_pre)) {
          inc <- TRUE
          break
        }
        if ((fit_post1 > fit_pre)) {
          inc <- TRUE
          break
        }
        
      }
    }
    
  }
  
  if (num_het_loci == 0) {
    perc <- 0
  } else {
    perc <- (num_inc / num_het_loci)
  }
  df$segAlleles[gen] <- num_het_loci
  df$percFitInc[gen] <- perc
  pop <- selectCross(pop=pop, trait=oneTraitFitFunc, nInd=popSize*(1-selIntensity), nCrosses=popSize)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
[1] 11
[1] 12
[1] 13
[1] 14
[1] 15
[1] 16
[1] 17
[1] 18
[1] 19
[1] 20
par(mar = c(5, 5, 3, 5) + 0.3)
plot(df$gen, df$fitness, type="l", lwd = "3", col=2, xlab = "Generation", ylab = "Fitness")
par(new = TRUE) 
plot(df$gen, df$percFitInc, type="l", lwd = "3", col = 3, axes = FALSE, xlab = "", ylab = "") 
axis(side = 4, at = pretty(range(df$percFitInc)))
mtext("% of Segregating alleles that are Beneficial", side = 4, line = 3)
par(xpd=TRUE)
legend("right",
  c("Trait Value", "% Alleles"),
       lty = 1,
       lwd = 3,
       col = 2:3)


ggplot(data=df, aes(x=gen, y=segAlleles)) +
  geom_line() +
  labs(x = "Generation", y = "Num Segregating Alleles")

Simulate several adaptive walks with different population sizes

nGens = 50
popSizes = c(10,200,1000)
nChr = 10
nSegSites = 50
selIntensity = 0.5
nLoci = nSegSites * nChr

fig <- plot_ly()
fit_df <- data.frame(gen=1:nGens,
                   fitness=numeric(nGens),
                   traitValA=numeric(nGens),
                   traitValB=numeric(nGens))
for (p in c(1:length(popSizes))) {
  popSize = popSizes[p]
  founders = runMacs(
     nInd=popSize,
     nChr=nChr,
     segSites=nSegSites
  )
  SP <- SimParam$new(founders)
  SP$addTraitA(mean=c(-10,10), var=c(1,1), nQtlPerChr=10)
  
  SP$setVarE(H2=c(0.5,0.5))
  foundingPop <- newPop(founders, simParam = SP)
  
  pop <- foundingPop
  
  for(gen in 1:nGens) {
    fit_df$fitness[gen] <- mean(twoTraitFitFunc(pheno(pop)))
    fit_df$traitValA[gen] <- meanP(pop)[1]
    fit_df$traitValB[gen] <- meanP(pop)[2]
    pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=popSize*(1-selIntensity), nCrosses=popSize)
  }
  
  fig <- add_trace(
    fig,
    fit_df,
    name = popSize,
    x = fit_df$traitValA,
    y = fit_df$traitValB,
    z = fit_df$fitness,
    type = 'scatter3d',
    mode = 'lines',
    opacity = 1,
    color = p,
    line = list(width = 10)
  )
  
}

fig %>%
  layout(legend=list(title=list(text='Population Size')),
         scene = list(xaxis = list(title = "Trait A"),
                      yaxis = list(title = "Trait B"),
                      zaxis = list(title = "w"),
                      aspectmode='cube')) %>% hide_colorbar()
NA

Overlay an adaptive walk over a fitness landscape

set.seed(123)

nGens = 50
popSize = 200
nChr = 10
nSegSites = 50
nLoci = nSegSites * nChr

fig <- plot_ly()
fit_df <- data.frame(gen=1:nGens,
                   fitness=numeric(nGens),
                   traitValA=numeric(nGens),
                   traitValB=numeric(nGens))
founders = runMacs(
   nInd=popSize,
   nChr=nChr,
   segSites=nSegSites
)
SP <- SimParam$new(founders)
SP$addTraitA(mean=c(runif(1,-10,10),runif(1,-10,10)), var=c(1,1), nQtlPerChr=10)

SP$setVarE(H2=c(0.5,0.5))
foundingPop <- newPop(founders, simParam = SP)

pop <- foundingPop

for(gen in 1:nGens) {
  fit_df$fitness[gen] <- mean(twoTraitFitFunc(pheno(pop)))
  fit_df$traitValA[gen] <- meanP(pop)[1]
  fit_df$traitValB[gen] <- meanP(pop)[2]
  pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=popSize*0.5, nCrosses=popSize)
}

overlayWalkOnLandscape(fit_df, type="SURFACE", calculateFitnessTwoTrait)

TODO:

Simualate mutations for a single individual to figure out P(allelic substitution is favorable) - should reflect Fisher/Kimura

P(mutation gets fixed) - show that this matches Kimura

Compare adaptive walks for DH/inbred population where there are new mutations VS population w/standing genetic variation

Figure out how to do QTL mapping

nBurnInGens <- 20

fit_df <- data.frame(gen=1:nGens,
                   fitness=numeric(nGens),
                   traitValA=numeric(nGens),
                   traitValB=numeric(nGens))

pop <- foundingPop

# BURN-IN
for (gen in 1:nBurnInGens) {
  fit_df$fitness[gen] <- mean(twoTraitFitFunc(pheno(pop)))
  fit_df$traitValA[gen] <- meanP(pop)[1]
  fit_df$traitValB[gen] <- meanP(pop)[2]
  pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=popSize*0.5, nCrosses=popSize)
}

# Create 2 sub populations

popA_df <- data.frame(gen=1:nGens,
                   fitness=numeric(nGens),
                   traitValA=numeric(nGens),
                   traitValB=numeric(nGens))

popB_df <- data.frame(gen=1:nGens,
                   fitness=numeric(nGens),
                   traitValA=numeric(nGens),
                   traitValB=numeric(nGens))

# Figure out a better way to select randomly? or by traitA, traitB?
popA <- selectInd(pop, use="rand", nInd=popSize*0.1)
popB <- selectInd(pop, use="rand", nInd=popSize*0.1)

nSegGens <- 20
for (gen in 1:nSegGens) {
  popA_df$fitness[gen] <- mean(twoTraitFitFunc(pheno(popA)))
  popA_df$traitValA[gen] <- meanP(popA)[1]
  popA_df$traitValB[gen] <- meanP(popA)[2]
  popA <- selectCross(popA, trait=twoTraitFitFunc, nInd=nInd(popA)*0.5, nCrosses=nInd(popA))
  
  popB_df$fitness[gen] <- mean(twoTraitFitFunc(pheno(popB)))
  popB_df$traitValA[gen] <- meanP(popB)[1]
  popB_df$traitValB[gen] <- meanP(popB)[2]
  popB <- selectCross(popB, trait=twoTraitFitFunc, nInd=nInd(popB)*0.5, nCrosses=nInd(popB))
}

Pull out individuals from each population and cross them Create RILs Inspect genetic architecture + do QTL mapping Work in Progress

Figure out QTL Mapping

LS0tCnRpdGxlOiAiUGxheWluZyBBcm91bmQgd2l0aCBGaXRuZXNzIEZ1bmN0aW9ucyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKSW1wb3J0cwpgYGB7cn0KbGlicmFyeShBbHBoYVNpbVIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShwbG90bHkpCmxpYnJhcnkodmlyaWRpcykKcm0obGlzdCA9IGxzKCkpCmBgYAoKR2xvYmFsIFZhcmlhYmxlcwpgYGB7cn0KcG9wU2l6ZSA8LSA1MDAKbkNociA8LSAxMApuU2VnU2l0ZXMgPC0gMTAwCm5HZW5zIDwtIDUwCmBgYAoKRnVuY3Rpb25zCmBgYHtyfQpvbmVUcmFpdEZpdEZ1bmMgPC0gZnVuY3Rpb24oeCkgewogIHJldHVybiAoLSh4KV4yKSArIHJub3JtKDEsc2Q9MikKfQoKdHdvVHJhaXRGaXRGdW5jIDwtIGZ1bmN0aW9uKHgpIHsKICByZXMgPSAtKHhbLDFdXjIpIC0geFssMl1eMgogIHJldHVybiAocmVzKQp9CgpjYWxjdWxhdGVGaXRuZXNzVHdvVHJhaXQgPC0gZnVuY3Rpb24oeCx5KSB7CiAgcmV0dXJuICgoLSh4KV4yKSsoLSh5KV4yKSkKfQoKaGV0TG9jdXMgPC0gZnVuY3Rpb24obG9jdXMpIHsKICByZXR1cm4gKGxlbmd0aCh1bmlxdWUobG9jdXMpKSA+IDEpCn0KYGBgCgpQbG90dGluZyBGdW5jdGlvbnMKYGBge3J9CnRoZW1lIDwtIHRoZW1lKHBsb3QuYmFja2dyb3VuZCA9IGdncGxvdDI6OmVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGdncGxvdDI6OmVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgYXhpcy5saW5lID0gZ2dwbG90Mjo6ZWxlbWVudF9saW5lKGxpbmV3aWR0aCA9IDAuMiksCiAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICdib2xkJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMTIpLAogICAgICAgICAgICAgICBheGlzLnRleHQgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSAgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ2JsYWNrJyksCiAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoc2l6ZSAgPSAxMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gJ2JvbGQnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gJ2JsYWNrJykpCgojIEV4cGVjdHMgYSBkYXRhZnJhbWUgd2l0aCAyIGNvbHVtbnM6CiMgdHJhaXRWYWxBLCB0cmFpdFZhbEIgKGZvciB0eXBlICJDT05UT1VSIiksCiMgd2l0aCBhIDNyZCBjb2x1bW4gKGZpdG5lc3MpIChmb3IgdHlwZSAiU1VSRkFDRSIpCm92ZXJsYXlXYWxrT25MYW5kc2NhcGUgPC1mdW5jdGlvbihkZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9IkNPTlRPVVIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZml0Q2FsYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0QU1pbj0tMTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdEFNYXg9MTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdEJNaW49LTEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRCTWF4PTEwKSB7CgogIGZpdG5lc3NfeCA9IHNlcSh0cmFpdEFNaW4sdHJhaXRBTWF4LCBieT0wLjI1KQogIGZpdG5lc3NfeSA9IHNlcSh0cmFpdEJNaW4sdHJhaXRCTWF4LCBieT0wLjI1KQogIGZpdG5lc3NfeiA9IG91dGVyKGZpdG5lc3NfeCxmaXRuZXNzX3ksZml0Q2FsYykKICBmaWcgPC0gcGxvdF9seSgpCgogIGlmICh0eXBlID09ICJDT05UT1VSIikgewogICAgcGxvdF9seSgpICU+JQogICAgICBsYXlvdXQoeGF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEEiLCBjb25zdHJhaW4gPSAiZG9tYWluIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiLCBzY2FsZWFuY2hvcj0ieCIpKSAlPiUKICAgICAgYWRkX3RyYWNlKAogICAgICAgIGZpZywKICAgICAgICB4PWZpdG5lc3NfeCwKICAgICAgICB5PWZpdG5lc3NfeSwKICAgICAgICB6PWZpdG5lc3NfeiwKICAgICAgICB0eXBlPSdjb250b3VyJywKICAgICAgICBjb2xvcnNjYWxlID0gIlJkQnUiLAogICAgICAgIGNvbG9yYmFyPWxpc3QodGl0bGUgPSAidyIpLAogICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gJ2JsYWNrJywgd2lkdGggPSAxKSwKICAgICAgICBvcGFjaXR5PTEpICU+JQogICAgICBhZGRfdHJhY2UoCiAgICAgICAgZmlnLAogICAgICAgIGRmLAogICAgICAgIG5hbWUgPSBwb3BTaXplLAogICAgICAgIHggPSBkZiR0cmFpdFZhbEEsCiAgICAgICAgeSA9IGRmJHRyYWl0VmFsQiwKICAgICAgICB0eXBlPSdzY2F0dGVyJywKICAgICAgICBtb2RlID0gJ2xpbmVzJywKICAgICAgICBsaW5lID0gbGlzdChjb2xvciA9ICdibGFjaycsIHdpZHRoID0gNCwgZGFzaCA9ICdzb2xpZCcpLAogICAgICAgIG9wYWNpdHkgPSAxKQogIH0gZWxzZSBpZiAodHlwZSA9PSAiU1VSRkFDRSIpewogICAgcGxvdF9seSgpICU+JQogICAgICBsYXlvdXQoc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJUcmFpdCBBIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAidyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGFzcGVjdG1vZGU9J2N1YmUnKSkgJT4lCiAgICAgIGFkZF90cmFjZSgKICAgICAgICBmaWcsCiAgICAgICAgZGYsCiAgICAgICAgbmFtZSA9IHBvcFNpemUsCiAgICAgICAgeCA9IGRmJHRyYWl0VmFsQSwKICAgICAgICB5ID0gZGYkdHJhaXRWYWxCLAogICAgICAgIHogPSBkZiRmaXRuZXNzLAogICAgICAgIHR5cGUgPSAnc2NhdHRlcjNkJywKICAgICAgICBtb2RlID0gJ2xpbmVzJywKICAgICAgICBvcGFjaXR5ID0gMSwKICAgICAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDEwKQogICAgICApICU+JQogICAgICBhZGRfdHJhY2UoCiAgICAgICAgZmlnLAogICAgICAgIHg9Zml0bmVzc194LAogICAgICAgIHk9Zml0bmVzc195LAogICAgICAgIHo9Zml0bmVzc196LAogICAgICAgIHR5cGU9J3N1cmZhY2UnLAogICAgICAgIGNvbG9yYmFyPWxpc3QodGl0bGUgPSAidyIpLAogICAgICAgIGNvbG9ycyA9ICJQdUJ1R24iLAogICAgICAgIG9wYWNpdHk9MC45KQogICAgICAKICB9IGVsc2UgewogICAgcHJpbnQoIlR5cGUgTm90IFN1cHBvcnRlZCIpCiAgfQp9CgojIFBsb3QgYSBwb3B1bGF0aW9uIG9uIGEgYSAzZCBmaXRuZXNzIGNoYXJ0CiMgb25seSB3b3JrcyBmb3IgcG9wdWxhdGlvbnMgd2l0aCAyIHRyYWl0cwojIFRPRE86IGZpZ3VyZSBvdXQgaG93IHRvIGFkZCBjb2xvcmJhciBsYWJlbApwbG90M2RQb3B1bGF0aW9uRml0bmVzcyA8LSBmdW5jdGlvbihwb3AsIGZpdENhbGMpIHsKICBwb3BTaXplIDwtIG5JbmQocG9wKQogIGRmIDwtIGRhdGEuZnJhbWUodHJhaXRBPW51bWVyaWMocG9wU2l6ZSksCiAgICAgICAgICAgICAgICAgICB0cmFpdEI9bnVtZXJpYyhwb3BTaXplKSwKICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhwb3BTaXplKSkKICBmb3IgKGkgaW4gMTpwb3BTaXplKSB7CiAgICBkZiR0cmFpdEFbaV0gPC0gcGhlbm8ocG9wKVtpLDFdCiAgICBkZiR0cmFpdEJbaV0gPC0gcGhlbm8ocG9wKVtpLDJdCiAgICBkZiRmaXRuZXNzW2ldIDwtIGZpdENhbGMoZGYkdHJhaXRBW2ldLCBkZiR0cmFpdEJbaV0pCiAgfQogIGZpZyA8LSBwbG90X2x5KCkKICAKICBwbG90X2x5KCkgJT4lIAogICAgbGF5b3V0KHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgMSIpLAogICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgMiIpLAogICAgICAgICAgICAgICAgICAgICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAidyIpLAogICAgICAgICAgICAgICAgICAgICAgICBhc3BlY3Rtb2RlPSdjdWJlJykpICU+JQogICAgYWRkX3RyYWNlKAogICAgICBmaWcsCiAgICAgIGRmLAogICAgICBuYW1lID0gcG9wU2l6ZSwKICAgICAgeCA9IGRmJHRyYWl0QSwKICAgICAgeSA9IGRmJHRyYWl0QiwKICAgICAgeiA9IGRmJGZpdG5lc3MsCiAgICAgIHR5cGUgPSAnc2NhdHRlcjNkJywKICAgICAgbW9kZSA9ICdtYXJrZXJzJywKICAgICAgY29sb3I9ZGYkZml0bmVzcykKfQoKIyBQbG90cyB0aGUgdHJhaXQgYXJjaGl0ZWN0dXJlIG9uIGEgZ2VuZXRpYyBiYXNpcwojIFdvcmtzIGZvciAxIG9yIDIgdHJhaXQgcG9wdWxhdGlvbnMKcGxvdFRyYWl0QXJjaGl0ZWN0dXJlIDwtIGZ1bmN0aW9uKHBvcCwgZml0RnVuYykgewogIGZpdF9wcmUgPC0gbWVhbihmaXRGdW5jKGd2KHBvcCkpKQogIGdlbm8gPC0gcHVsbFNlZ1NpdGVHZW5vKHBvcCkKICBjb2xzIDwtIGNvbG5hbWVzKGdlbm8pCiAgbkxvY2kgPC0gbGVuZ3RoKGNvbHMpCiAgcG9wU2l6ZSA8LSBucm93KGdlbm8pCiAgZWZmX3NpemVzIDwtIGRhdGEuZnJhbWUoaWQgPSBjaGFyYWN0ZXIobkxvY2kpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGVmZl9zaXplPW51bWVyaWMobkxvY2kpKQogIGZvciAobCBpbiAxOm5Mb2NpKSB7CiAgICBpZCA8LSBjb2xzW2xdCiAgICBlZmZfc2l6ZXMkaWRbbF0gPC0gaWQKICAgIGxvY3VzID0gZ2Vub1ssbF0KICAgIGlmICghaGV0TG9jdXMobG9jdXMpKSB7CiAgICAgIGVmZl9zaXplcyRlZmZfc2l6ZVtsXSA8LSBOQQogICAgICBuZXh0CiAgICB9CiAgICBzdHJzID0gdW5saXN0KHN0cnNwbGl0KGlkLCAiXyIpKQogICAgY2hyID0gc3RydG9pKHN0cnNbMV0pCiAgICBzaXRlID0gc3RydG9pKHN0cnNbMl0pCiAgICBwb3AxIDwtIGVkaXRHZW5vbWUocG9wLCBpbmQ9YygxOnBvcFNpemUpLCBjaHI9Y2hyLCBzZWdTaXRlcz1zaXRlLCBhbGxlbGU9MCwgc2ltUGFyYW09U1ApCiAgICBwb3AyIDwtIGVkaXRHZW5vbWUocG9wLCBpbmQ9YygxOnBvcFNpemUpLCBjaHI9Y2hyLCBzZWdTaXRlcz1zaXRlLCBhbGxlbGU9MSwgc2ltUGFyYW09U1ApCiAgICBlZmZlY3Rfc2l6ZV8xIDwtIG1lYW4oZml0RnVuYyhndihwb3AxKSkpIC0gZml0X3ByZQogICAgZWZmZWN0X3NpemVfMiA8LSBtZWFuKGZpdEZ1bmMoZ3YocG9wMikpKSAtIGZpdF9wcmUKICAgIGlmIChlZmZlY3Rfc2l6ZV8xID4gMCkgewogICAgICBlZmZfc2l6ZXMkZWZmX3NpemVbbF0gPC0gZWZmZWN0X3NpemVfMQogICAgfSBlbHNlIGlmIChlZmZlY3Rfc2l6ZV8yID4gMSkgewogICAgICBlZmZfc2l6ZXMkZWZmX3NpemVbbF0gPC0gZWZmZWN0X3NpemVfMgogICAgfSBlbHNlIHsKICAgICAgZWZmX3NpemVzJGVmZl9zaXplW2xdIDwtIDAKICAgIH0KICB9CiAgZWZmX3NpemVzIDwtIGVmZl9zaXplc1thcHBseShlZmZfc2l6ZXMhPTAsIDEsIGFsbCksXQogIGVmZl9zaXplcyA8LSBuYS5vbWl0KGVmZl9zaXplcykKICBnZ3Bsb3QoZGF0YT1lZmZfc2l6ZXMsIGFlcyh4PXJlb3JkZXIoaWQsIC1lZmZfc2l6ZSksIHk9ZWZmX3NpemUpKSArCiAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsKICAgIGxhYnMoeCA9ICJWYXJpYW50IElkIiwgeSA9ICJFZmZlY3QgU2l6ZSIpICsKICAgIHRoZW1lICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMC41LCBoanVzdD0xLCBzaXplID0gNiwgbWFyZ2luID0gbWFyZ2luKGIgPSAxMCkpLAogICAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKGw9MTAsIHI9MTApKSkKfQoKIyBTaG93IHRoZSBmaXRuZXNzIG9mIGEgcG9wdWxhdGlvbgpwbG90X2ZpdG5lc3MgPC0gZnVuY3Rpb24oZGYpIHsKICBnZ3Bsb3QoZGF0YT1kZiwgYWVzKHg9Z2VuLCB5PWZpdG5lc3MpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgbGFicyh4ID0gIkdlbmVyYXRpb24iLCB5ID0gIkZpdG5lc3MiKQp9CgojIFBsb3QgR2VuZXRpYyBWYWx1ZXMgZm9yIFR3byBUcmFpdHMKcGxvdF9oaXN0IDwtIGZ1bmN0aW9uKHBvcCkgewogIGd2X2EgPSBndihwb3ApWywxXQogIGd2X2IgPSBndihwb3ApWywyXQogIGlkeCA9IGMoMTpsZW5ndGgoZ3ZfYSkpCiAgZGYgPC0gZGF0YS5mcmFtZShndl9hLCBndl9iKQogIGRmIDwtIG1lbHQoYXMuZGF0YS50YWJsZShkZikpCiAgZ2dwbG90KGRmLCBhZXMoeD12YWx1ZSwgY29sb3I9dmFyaWFibGUpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTEsIHBvc2l0aW9uPSdpZGVudGl0eScpCn0KYGBgCgpQbGF5IGFyb3VuZCB3aXRoIGRpZmZlcmVudCBmaXRuZXNzIGZ1bmN0aW9ucwpgYGB7cn0KCnggPSBzZXEoLTEwMCwxMDAsIGJ5PTEpCnkgPSBvbmVUcmFpdEZpdEZ1bmMoeCkKCmRmIDwtIGRhdGEuZnJhbWUoeD14LCB5PXkpCmdncGxvdChkYXRhPWRmLCBhZXMoeD14LCB5PXkpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgbGFicyh4ID0gIlRyYWl0IFZhbHVlIiwgeSA9ICJGaXRuZXNzIikKCmZpdG5lc3NfeCA9IHNlcSgtMTAsMTAsIGJ5PTAuMjUpCmZpdG5lc3NfeSA9IHNlcSgtMTAsMTAsIGJ5PTAuMjUpCmZpdG5lc3NfeiA9IG91dGVyKGZpdG5lc3NfeCxmaXRuZXNzX3ksY2FsY3VsYXRlRml0bmVzc1R3b1RyYWl0KQoKCnBsb3RfbHkoeD1maXRuZXNzX3gsCiAgICAgICAgeT1maXRuZXNzX3ksCiAgICAgICAgej1maXRuZXNzX3osCiAgICAgICAgdHlwZT0nc3VyZmFjZScsCiAgICAgICAgY29sb3JzID0gIlB1QnVHbiIsCiAgICAgICAgb3BhY2l0eT0xKSAlPiUKICBsYXlvdXQoc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJUcmFpdCBBIiksCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQiIpLAogICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gInciKSwKICAgICAgICAgICAgICAgICAgICAgIGFzcGVjdG1vZGU9J2N1YmUnKSkKCiMgVmFsaWQgYXJndW1lbnRzOgojIGNvbG9ycyA9ICJQdUJ1R24iCiMgY29sb3JzID0gY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwgIm9yYW5nZSIpKSgxNSkKIyBjb2xvcnMgPSBtYWdtYSg1MCwgYWxwaGEgPSAxLCBiZWdpbiA9IDAsIGVuZCA9IDEsIGRpcmVjdGlvbiA9IDEpICh2aXJpZGlzLCBwbGFzbWEsIG1hZ21hLCBpbmZlcm5vKQpgYGAKClNob3cgdGhhdCBhbGwgaW5kaXZpZHVhbHMgY29udmVyZ2UgdG8gYSBmaXRuZXNzIG1heGltdW0KYGBge3J9CnN0YXJ0X3RyYWl0X3ZhbCA8LSBjKC01MCwtMjUsLTEwLDEwLDI1LDUwKQpudW1TaW1zIDwtIGxlbmd0aChzdGFydF90cmFpdF92YWwpCnBvcFNpemUgPC0gNTAwCm5TZWdTaXRlcyA8LSA1MAoKZGYgPC0gZGF0YS5mcmFtZShnZW49cmVwKDE6bkdlbnMsIHRpbWVzPW51bVNpbXMpLAogICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhuR2VucypudW1TaW1zKSwKICAgICAgICAgICAgICAgICBpbml0aWFsVmFsID0gbnVtZXJpYyhuR2VucypudW1TaW1zKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbD1udW1lcmljKG5HZW5zKm51bVNpbXMpKQoKZm9yIChzIGluIDE6bnVtU2ltcykgewogIGluaXRWYWwgPC0gc3RhcnRfdHJhaXRfdmFsW3NdCiAgZm91bmRlcnMgPSBxdWlja0hhcGxvKAogICAgIG5JbmQ9cG9wU2l6ZSwKICAgICBuQ2hyPW5DaHIsCiAgICAgc2VnU2l0ZXM9blNlZ1NpdGVzCiAgKQogIFNQIDwtIFNpbVBhcmFtJG5ldyhmb3VuZGVycykKICBTUCRhZGRUcmFpdEEoCiAgICAxMCwKICAgIG1lYW49aW5pdFZhbCwKICAgIHZhcj1hYnMoaW5pdFZhbC8yKQogICkKICBTUCRzZXRWYXJFKEgyPTAuNSkKICBwb3AgPC0gbmV3UG9wKGZvdW5kZXJzLCBzaW1QYXJhbT1TUCkKICAKICBmb3IoZ2VuIGluIDE6bkdlbnMpIHsKICAgIGlkeCA9IChzLTEpKm5HZW5zICsgZ2VuCiAgICBkZiRmaXRuZXNzW2lkeF0gPC0gb25lVHJhaXRGaXRGdW5jKHBoZW5vKHBvcCkpCiAgICBkZiR0cmFpdFZhbFtpZHhdIDwtIG1lYW5QKHBvcCkKICAgIGRmJGluaXRpYWxWYWxbaWR4XSA8LSBpbml0VmFsCiAgICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wPXBvcCwgdHJhaXQ9b25lVHJhaXRGaXRGdW5jLCBuSW5kPXBvcFNpemUvMiwgbkNyb3NzZXM9cG9wU2l6ZSkKICB9Cn0KCmRmJGluaXRpYWxWYWwgPSBhcy5jaGFyYWN0ZXIoZGYkaW5pdGlhbFZhbCkKZ2dwbG90KGRhdGE9ZGYsIGFlcyh4PWdlbiwgeT10cmFpdFZhbCkpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gaW5pdGlhbFZhbCkpICsKICBsYWJzKHggPSAiR2VuZXJhdGlvbiIsIHkgPSAiVHJhaXQgVmFsdWUiKQoKYGBgCgpQbG90IFRyYWl0IFZhbHVlIGFnYWluc3QgRml0bmVzcwpgYGB7cn0KbkdlbnMgPC0gNTAKbkluZCA8LSA1MDAKblNlZ1NpdGVzIDwtIDEwMDAKCgpmb3VuZGVycyA9IHF1aWNrSGFwbG8oCiAgIG5JbmQ9bkluZCwKICAgbkNocj1uQ2hyLAogICBzZWdTaXRlcz1uU2VnU2l0ZXMKKQpTUCA8LSBTaW1QYXJhbSRuZXcoZm91bmRlcnMpClNQJGFkZFRyYWl0QSgKICAxMCwKICBtZWFuPTUwLAogIHZhcj1hYnMoaW5pdFZhbC8yKQopClNQJHNldFZhckUoSDI9MC41KQpwb3AgPC0gbmV3UG9wKGZvdW5kZXJzLCBzaW1QYXJhbT1TUCkKCmZpdF9kZiA8LSBkYXRhLmZyYW1lKGdlbj0xOm5HZW5zLAogICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhuR2VucyksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWw9bnVtZXJpYyhuR2VucykpCmZvcihnZW4gaW4gMTpuR2VucykgewogIGZpdF9kZiRmaXRuZXNzW2dlbl0gPC0gZml0bmVzc0Z1bmMobWVhblAocG9wKSkKICBmaXRfZGYkdHJhaXRWYWxbZ2VuXSA8LSBtZWFuUChwb3ApCiAgcG9wIDwtIHNlbGVjdENyb3NzKHBvcD1wb3AsIHRyYWl0PW9uZVRyYWl0Rml0RnVuYywgbkluZD1wb3BTaXplLzIsIG5Dcm9zc2VzPXBvcFNpemUpCn0KCnBhcihtYXIgPSBjKDUsIDUsIDMsIDUpICsgMC4zKQpwbG90KGZpdF9kZiRnZW4sIGZpdF9kZiR0cmFpdFZhbCwgdHlwZT0ibCIsIGx3ZCA9ICIzIiwgY29sPTIsIHhsYWIgPSAiR2VuZXJhdGlvbiIsIHlsYWIgPSAiVHJhaXQgVmFsdWUiKSAgIApwYXIobmV3ID0gVFJVRSkgCnBsb3QoZml0X2RmJGdlbiwgZml0X2RmJGZpdG5lc3MsIHR5cGU9ImwiLCBsd2QgPSAiMyIsIGNvbCA9IDMsIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpIApheGlzKHNpZGUgPSA0LCBhdCA9IHByZXR0eShyYW5nZShmaXRfZGYkZml0bmVzcykpKQptdGV4dCgiRml0bmVzcyIsIHNpZGUgPSA0LCBsaW5lID0gMykKcGFyKHhwZD1UUlVFKQpsZWdlbmQoInJpZ2h0IiwKICBjKCJUcmFpdCBWYWx1ZSIsICJGaXRuZXNzIiksCiAgICAgICBsdHkgPSAxLAogICAgICAgbHdkID0gMywKICAgICAgIGNvbCA9IDI6MykKYGBgCgpDcmVhdGUgMSBUcmFpdCBQb3B1bGF0aW9uCmBgYHtyfQpwb3BTaXplID0gMjAwCm5TZWdTaXRlcyA9IDUwCm5Mb2NpID0gblNlZ1NpdGVzICogbkNocgoKZm91bmRlcnMgPSBydW5NYWNzKAogICBuSW5kPXBvcFNpemUsCiAgIG5DaHI9bkNociwKICAgc2VnU2l0ZXM9blNlZ1NpdGVzCikKU1AgPC0gU2ltUGFyYW0kbmV3KGZvdW5kZXJzKQpTUCRhZGRUcmFpdEEoCiAgbWVhbj0yMCwKICB2YXI9NSwKICBuUXRsUGVyQ2hyID0gMTAgIyBjaGFuZ2luZyB0aGlzIGNoYW5nZXMgZWZmZWN0IHNpemU/IDEwMCBRVEwKKQoKU1Akc2V0VmFyRShIMj0wLjUpCmZvdW5kaW5nUG9wIDwtIG5ld1BvcChmb3VuZGVycywgc2ltUGFyYW0gPSBTUCkKcGxvdFRyYWl0QXJjaGl0ZWN0dXJlKGZvdW5kaW5nUG9wLCBvbmVUcmFpdEZpdEZ1bmMpCmBgYAoKQ3JlYXRlIDIgVHJhaXQgUG9wdWxhdGlvbgpgYGB7cn0Kc2V0LnNlZWQoMTIzKQpwb3BTaXplID0gMTAwMApuU2VnU2l0ZXMgPSAxMDAKCmZvdW5kZXJzID0gcnVuTWFjcygKICAgbkluZD1wb3BTaXplLAogICBuQ2hyPW5DaHIsCiAgIHNlZ1NpdGVzPW5TZWdTaXRlcwopClNQIDwtIFNpbVBhcmFtJG5ldyhmb3VuZGVycykKU1AkYWRkVHJhaXRBKG1lYW49YyhydW5pZigxLC0xMCwxMCkscnVuaWYoMSwtMTAsMTApKSwgdmFyPWMoMSwxKSwgblF0bFBlckNocj0xMCkKClNQJHNldFZhckUoSDI9YygwLjUsMC41KSkKZm91bmRpbmdQb3AgPC0gbmV3UG9wKGZvdW5kZXJzLCBzaW1QYXJhbSA9IFNQKQoKI3Bsb3QzZFBvcHVsYXRpb25GaXRuZXNzKGZvdW5kaW5nUG9wLCBjYWxjdWxhdGVGaXRuZXNzVHdvVHJhaXQpCiNwbG90VHJhaXRBcmNoaXRlY3R1cmUoZm91bmRpbmdQb3AsIHR3b1RyYWl0Rml0RnVuYykKYGBgCgpPdXQgb2YgYWxsZWxlcyBsZWZ0IGluIHRoZSBwb3B1bGF0aW9uLCBob3cgbWFueSBvZiB0aGVtIHdvdWxkIGluY3JlYXNlIGZpdG5lc3M/Ck5leHQ6IGhvdyBkb2VzIHRoaXMgY2hhbmdlIHdpdGggc2l6ZSBvZiBhbGxlbGU/IExvb2sgYXQgZWRpdEdlbm9tZVRvcFF0bCgpCk5leHQ6IGhvdyBkb2VzIHNpemUgb2YgJ2ZpdG5lc3MgZGVsdGEnIGNoYW5nZSBvdmVyIGdlbmVyYXRpb25zPwpgYGB7cn0KbkdlbnMgPSA1MApwb3AgPC0gZm91bmRpbmdQb3AKc2VsSW50ZW5zaXR5IDwtIDAuMgoKY29scyA9IGNvbG5hbWVzKHB1bGxTZWdTaXRlR2Vubyhwb3ApKQpkZiA8LSBkYXRhLmZyYW1lKGdlbj1yZXAoMTpuR2VucyksCiAgICAgICAgICAgICAgICAgZml0bmVzcz1udW1lcmljKG5HZW5zKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbD1udW1lcmljKG5HZW5zKSwKICAgICAgICAgICAgICAgICBwZXJjRml0SW5jPW51bWVyaWMobkdlbnMpLAogICAgICAgICAgICAgICAgIHNlZ0FsbGVsZXM9bnVtZXJpYyhuR2VucykpCgpmb3IgKGdlbiBpbiAxOm5HZW5zKSB7CiAgcHJpbnQoZ2VuKQogIGRmJGZpdG5lc3NbZ2VuXSA8LSBmaXRuZXNzRnVuYyhtZWFuRyhwb3ApKQogIGRmJHRyYWl0VmFsW2dlbl0gPC0gbWVhbkcocG9wKQogIG51bV9oZXRfbG9jaSA9IDAKICBudW1faW5jID0gMAogIGdlbm8gPC0gcHVsbFNlZ1NpdGVHZW5vKHBvcCkKICBmb3IgKGwgaW4gMTpuTG9jaSkgewogICAgc3RycyA9IHVubGlzdChzdHJzcGxpdChjb2xzW2xdLCAiXyIpKQogICAgY2hyID0gc3RydG9pKHN0cnNbMV0pCiAgICBzaXRlID0gc3RydG9pKHN0cnNbMl0pCiAgICBsb2N1cyA9IGdlbm9bLGxdCiAgICBpbmMgPC0gRkFMU0UKICAgIGlmIChoZXRMb2N1cyhsb2N1cykpIHsKICAgICAgbnVtX2hldF9sb2NpIDwtIG51bV9oZXRfbG9jaSArIDEKICAgICAgZml0X3ByZSA8LSBmaXRuZXNzRnVuYyhtZWFuRyhwb3ApKQogICAgICBwb3AwIDwtIGVkaXRHZW5vbWUocG9wLCBpbmQ9YygxOnBvcFNpemUpLCBjaHI9Y2hyLCBzZWdTaXRlcz1zaXRlLCBhbGxlbGU9MCwgc2ltUGFyYW09U1ApCiAgICAgIHBvcDEgPC0gZWRpdEdlbm9tZShwb3AsIGluZD1jKDE6cG9wU2l6ZSksIGNocj1jaHIsIHNlZ1NpdGVzPXNpdGUsIGFsbGVsZT0xLCBzaW1QYXJhbT1TUCkKICAgICAgZml0X3Bvc3QwIDwtIGZpdG5lc3NGdW5jKG1lYW5HKHBvcDApKQogICAgICBmaXRfcG9zdDEgPC0gZml0bmVzc0Z1bmMobWVhbkcocG9wMSkpCiAgICAgIGlmICgoZml0X3Bvc3QwID4gZml0X3ByZSkgfHwgKGZpdF9wb3N0MSA+IGZpdF9wcmUpICkgewogICAgICAgIGluYyA8LSBUUlVFCiAgICAgIH0KICAgIH0KICAgIGlmIChpbmMgPT0gVFJVRSkgewogICAgICBudW1faW5jIDwtIG51bV9pbmMgKyAxCiAgICB9CiAgICBpZiAoRkFMU0UpIHsKICAgICAgZm9yIChpIGluIDE6bkluZChwb3ApKSB7CiAgICAgICAgaW5kID0gZ2Vub1tpLF0KICAgICAgICBmaXRfcHJlIDwtIGZpdG5lc3NGdW5jKHBvcEBndltpXSkKICAgICAgICBwb3AwIDwtIGVkaXRHZW5vbWUocG9wLCBpbmQ9aSwgY2hyPWNociwgc2VnU2l0ZXM9c2l0ZSwgYWxsZWxlPTAsIHNpbVBhcmFtPVNQKQogICAgICAgIHBvcDEgPC0gZWRpdEdlbm9tZShwb3AsIGluZD1pLCBjaHI9Y2hyLCBzZWdTaXRlcz1zaXRlLCBhbGxlbGU9MSwgc2ltUGFyYW09U1ApCiAgICAgICAgCiAgICAgICAgZml0X3Bvc3QwIDwtIGZpdG5lc3NGdW5jKHBvcDBAZ3ZbaV0pCiAgICAgICAgZml0X3Bvc3QxIDwtIGZpdG5lc3NGdW5jKHBvcDFAZ3ZbaV0pCiAgICAgICAgaWYgKChmaXRfcG9zdDAgPiBmaXRfcHJlKSkgewogICAgICAgICAgaW5jIDwtIFRSVUUKICAgICAgICAgIGJyZWFrCiAgICAgICAgfQogICAgICAgIGlmICgoZml0X3Bvc3QxID4gZml0X3ByZSkpIHsKICAgICAgICAgIGluYyA8LSBUUlVFCiAgICAgICAgICBicmVhawogICAgICAgIH0KICAgICAgICAKICAgICAgfQogICAgfQogICAgCiAgfQogIAogIGlmIChudW1faGV0X2xvY2kgPT0gMCkgewogICAgcGVyYyA8LSAwCiAgfSBlbHNlIHsKICAgIHBlcmMgPC0gKG51bV9pbmMgLyBudW1faGV0X2xvY2kpCiAgfQogIGRmJHNlZ0FsbGVsZXNbZ2VuXSA8LSBudW1faGV0X2xvY2kKICBkZiRwZXJjRml0SW5jW2dlbl0gPC0gcGVyYwogIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3A9cG9wLCB0cmFpdD1vbmVUcmFpdEZpdEZ1bmMsIG5JbmQ9cG9wU2l6ZSooMS1zZWxJbnRlbnNpdHkpLCBuQ3Jvc3Nlcz1wb3BTaXplKQp9CnBhcihtYXIgPSBjKDUsIDUsIDMsIDUpICsgMC4zKQpwbG90KGRmJGdlbiwgZGYkZml0bmVzcywgdHlwZT0ibCIsIGx3ZCA9ICIzIiwgY29sPTIsIHhsYWIgPSAiR2VuZXJhdGlvbiIsIHlsYWIgPSAiRml0bmVzcyIpCnBhcihuZXcgPSBUUlVFKSAKcGxvdChkZiRnZW4sIGRmJHBlcmNGaXRJbmMsIHR5cGU9ImwiLCBsd2QgPSAiMyIsIGNvbCA9IDMsIGF4ZXMgPSBGQUxTRSwgeGxhYiA9ICIiLCB5bGFiID0gIiIpIApheGlzKHNpZGUgPSA0LCBhdCA9IHByZXR0eShyYW5nZShkZiRwZXJjRml0SW5jKSkpCm10ZXh0KCIlIG9mIFNlZ3JlZ2F0aW5nIGFsbGVsZXMgdGhhdCBhcmUgQmVuZWZpY2lhbCIsIHNpZGUgPSA0LCBsaW5lID0gMykKcGFyKHhwZD1UUlVFKQpsZWdlbmQoInJpZ2h0IiwKICBjKCJUcmFpdCBWYWx1ZSIsICIlIEFsbGVsZXMiKSwKICAgICAgIGx0eSA9IDEsCiAgICAgICBsd2QgPSAzLAogICAgICAgY29sID0gMjozKQoKZ2dwbG90KGRhdGE9ZGYsIGFlcyh4PWdlbiwgeT1zZWdBbGxlbGVzKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHggPSAiR2VuZXJhdGlvbiIsIHkgPSAiTnVtIFNlZ3JlZ2F0aW5nIEFsbGVsZXMiKQoKYGBgCgpTaW11bGF0ZSBzZXZlcmFsIGFkYXB0aXZlIHdhbGtzIHdpdGggZGlmZmVyZW50IHBvcHVsYXRpb24gc2l6ZXMKYGBge3J9Cm5HZW5zID0gNTAKcG9wU2l6ZXMgPSBjKDEwLDIwMCwxMDAwKQpuQ2hyID0gMTAKblNlZ1NpdGVzID0gNTAKc2VsSW50ZW5zaXR5ID0gMC41Cm5Mb2NpID0gblNlZ1NpdGVzICogbkNocgoKZmlnIDwtIHBsb3RfbHkoKQpmaXRfZGYgPC0gZGF0YS5mcmFtZShnZW49MTpuR2VucywKICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhuR2VucyksCiAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9bnVtZXJpYyhuR2VucyksCiAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEI9bnVtZXJpYyhuR2VucykpCmZvciAocCBpbiBjKDE6bGVuZ3RoKHBvcFNpemVzKSkpIHsKICBwb3BTaXplID0gcG9wU2l6ZXNbcF0KICBmb3VuZGVycyA9IHJ1bk1hY3MoCiAgICAgbkluZD1wb3BTaXplLAogICAgIG5DaHI9bkNociwKICAgICBzZWdTaXRlcz1uU2VnU2l0ZXMKICApCiAgU1AgPC0gU2ltUGFyYW0kbmV3KGZvdW5kZXJzKQogIFNQJGFkZFRyYWl0QShtZWFuPWMoLTEwLDEwKSwgdmFyPWMoMSwxKSwgblF0bFBlckNocj0xMCkKICAKICBTUCRzZXRWYXJFKEgyPWMoMC41LDAuNSkpCiAgZm91bmRpbmdQb3AgPC0gbmV3UG9wKGZvdW5kZXJzLCBzaW1QYXJhbSA9IFNQKQogIAogIHBvcCA8LSBmb3VuZGluZ1BvcAogIAogIGZvcihnZW4gaW4gMTpuR2VucykgewogICAgZml0X2RmJGZpdG5lc3NbZ2VuXSA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3ApKSkKICAgIGZpdF9kZiR0cmFpdFZhbEFbZ2VuXSA8LSBtZWFuUChwb3ApWzFdCiAgICBmaXRfZGYkdHJhaXRWYWxCW2dlbl0gPC0gbWVhblAocG9wKVsyXQogICAgcG9wIDwtIHNlbGVjdENyb3NzKHBvcCwgdHJhaXQ9dHdvVHJhaXRGaXRGdW5jLCBuSW5kPXBvcFNpemUqKDEtc2VsSW50ZW5zaXR5KSwgbkNyb3NzZXM9cG9wU2l6ZSkKICB9CiAgCiAgZmlnIDwtIGFkZF90cmFjZSgKICAgIGZpZywKICAgIGZpdF9kZiwKICAgIG5hbWUgPSBwb3BTaXplLAogICAgeCA9IGZpdF9kZiR0cmFpdFZhbEEsCiAgICB5ID0gZml0X2RmJHRyYWl0VmFsQiwKICAgIHogPSBmaXRfZGYkZml0bmVzcywKICAgIHR5cGUgPSAnc2NhdHRlcjNkJywKICAgIG1vZGUgPSAnbGluZXMnLAogICAgb3BhY2l0eSA9IDEsCiAgICBjb2xvciA9IHAsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDEwKQogICkKICAKfQoKZmlnICU+JQogIGxheW91dChsZWdlbmQ9bGlzdCh0aXRsZT1saXN0KHRleHQ9J1BvcHVsYXRpb24gU2l6ZScpKSwKICAgICAgICAgc2NlbmUgPSBsaXN0KHhheGlzID0gbGlzdCh0aXRsZSA9ICJUcmFpdCBBIiksCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQiIpLAogICAgICAgICAgICAgICAgICAgICAgemF4aXMgPSBsaXN0KHRpdGxlID0gInciKSwKICAgICAgICAgICAgICAgICAgICAgIGFzcGVjdG1vZGU9J2N1YmUnKSkgJT4lIGhpZGVfY29sb3JiYXIoKQogIApgYGAKCk92ZXJsYXkgYW4gYWRhcHRpdmUgd2FsayBvdmVyIGEgZml0bmVzcyBsYW5kc2NhcGUKYGBge3J9CnNldC5zZWVkKDEyMykKCm5HZW5zID0gNTAKcG9wU2l6ZSA9IDIwMApuQ2hyID0gMTAKblNlZ1NpdGVzID0gNTAKbkxvY2kgPSBuU2VnU2l0ZXMgKiBuQ2hyCgpmaWcgPC0gcGxvdF9seSgpCmZpdF9kZiA8LSBkYXRhLmZyYW1lKGdlbj0xOm5HZW5zLAogICAgICAgICAgICAgICAgICAgZml0bmVzcz1udW1lcmljKG5HZW5zKSwKICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1udW1lcmljKG5HZW5zKSwKICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1udW1lcmljKG5HZW5zKSkKZm91bmRlcnMgPSBydW5NYWNzKAogICBuSW5kPXBvcFNpemUsCiAgIG5DaHI9bkNociwKICAgc2VnU2l0ZXM9blNlZ1NpdGVzCikKU1AgPC0gU2ltUGFyYW0kbmV3KGZvdW5kZXJzKQpTUCRhZGRUcmFpdEEobWVhbj1jKHJ1bmlmKDEsLTEwLDEwKSxydW5pZigxLC0xMCwxMCkpLCB2YXI9YygxLDEpLCBuUXRsUGVyQ2hyPTEwKQoKU1Akc2V0VmFyRShIMj1jKDAuNSwwLjUpKQpmb3VuZGluZ1BvcCA8LSBuZXdQb3AoZm91bmRlcnMsIHNpbVBhcmFtID0gU1ApCgpwb3AgPC0gZm91bmRpbmdQb3AKCmZvcihnZW4gaW4gMTpuR2VucykgewogIGZpdF9kZiRmaXRuZXNzW2dlbl0gPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wKSkpCiAgZml0X2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5QKHBvcClbMV0KICBmaXRfZGYkdHJhaXRWYWxCW2dlbl0gPC0gbWVhblAocG9wKVsyXQogIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3AsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1wb3BTaXplKjAuNSwgbkNyb3NzZXM9cG9wU2l6ZSkKfQoKb3ZlcmxheVdhbGtPbkxhbmRzY2FwZShmaXRfZGYsIHR5cGU9IlNVUkZBQ0UiLCBjYWxjdWxhdGVGaXRuZXNzVHdvVHJhaXQpCmBgYAoKClRPRE86CgpTaW11YWxhdGUgbXV0YXRpb25zIGZvciBhIHNpbmdsZSBpbmRpdmlkdWFsIHRvIGZpZ3VyZSBvdXQgUChhbGxlbGljIHN1YnN0aXR1dGlvbiBpcyBmYXZvcmFibGUpIC0gc2hvdWxkIHJlZmxlY3QgRmlzaGVyL0tpbXVyYQoKUChtdXRhdGlvbiBnZXRzIGZpeGVkKSAtIHNob3cgdGhhdCB0aGlzIG1hdGNoZXMgS2ltdXJhCgpDb21wYXJlIGFkYXB0aXZlIHdhbGtzIGZvciBESC9pbmJyZWQgcG9wdWxhdGlvbiB3aGVyZSB0aGVyZSBhcmUgbmV3IG11dGF0aW9ucyBWUwpwb3B1bGF0aW9uIHcvc3RhbmRpbmcgZ2VuZXRpYyB2YXJpYXRpb24KCkZpZ3VyZSBvdXQgaG93IHRvIGRvIFFUTCBtYXBwaW5nCmBgYHtyfQpuQnVybkluR2VucyA8LSAyMAoKZml0X2RmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bkdlbnMsCiAgICAgICAgICAgICAgICAgICBmaXRuZXNzPW51bWVyaWMobkdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPW51bWVyaWMobkdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW51bWVyaWMobkdlbnMpKQoKcG9wIDwtIGZvdW5kaW5nUG9wCgojIEJVUk4tSU4KZm9yIChnZW4gaW4gMTpuQnVybkluR2VucykgewogIGZpdF9kZiRmaXRuZXNzW2dlbl0gPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wKSkpCiAgZml0X2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5QKHBvcClbMV0KICBmaXRfZGYkdHJhaXRWYWxCW2dlbl0gPC0gbWVhblAocG9wKVsyXQogIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3AsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1wb3BTaXplKjAuNSwgbkNyb3NzZXM9cG9wU2l6ZSkKfQoKIyBDcmVhdGUgMiBzdWIgcG9wdWxhdGlvbnMKCnBvcEFfZGYgPC0gZGF0YS5mcmFtZShnZW49MTpuR2VucywKICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhuR2VucyksCiAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9bnVtZXJpYyhuR2VucyksCiAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEI9bnVtZXJpYyhuR2VucykpCgpwb3BCX2RmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bkdlbnMsCiAgICAgICAgICAgICAgICAgICBmaXRuZXNzPW51bWVyaWMobkdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPW51bWVyaWMobkdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW51bWVyaWMobkdlbnMpKQoKIyBGaWd1cmUgb3V0IGEgYmV0dGVyIHdheSB0byBzZWxlY3QgcmFuZG9tbHk/IG9yIGJ5IHRyYWl0QSwgdHJhaXRCPwpwb3BBIDwtIHNlbGVjdEluZChwb3AsIHVzZT0icmFuZCIsIG5JbmQ9cG9wU2l6ZSowLjEpCnBvcEIgPC0gc2VsZWN0SW5kKHBvcCwgdXNlPSJyYW5kIiwgbkluZD1wb3BTaXplKjAuMSkKCm5TZWdHZW5zIDwtIDIwCmZvciAoZ2VuIGluIDE6blNlZ0dlbnMpIHsKICBwb3BBX2RmJGZpdG5lc3NbZ2VuXSA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BBKSkpCiAgcG9wQV9kZiR0cmFpdFZhbEFbZ2VuXSA8LSBtZWFuUChwb3BBKVsxXQogIHBvcEFfZGYkdHJhaXRWYWxCW2dlbl0gPC0gbWVhblAocG9wQSlbMl0KICBwb3BBIDwtIHNlbGVjdENyb3NzKHBvcEEsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uSW5kKHBvcEEpKjAuNSwgbkNyb3NzZXM9bkluZChwb3BBKSkKICAKICBwb3BCX2RmJGZpdG5lc3NbZ2VuXSA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BCKSkpCiAgcG9wQl9kZiR0cmFpdFZhbEFbZ2VuXSA8LSBtZWFuUChwb3BCKVsxXQogIHBvcEJfZGYkdHJhaXRWYWxCW2dlbl0gPC0gbWVhblAocG9wQilbMl0KICBwb3BCIDwtIHNlbGVjdENyb3NzKHBvcEIsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uSW5kKHBvcEIpKjAuNSwgbkNyb3NzZXM9bkluZChwb3BCKSkKfQpgYGAKClB1bGwgb3V0IGluZGl2aWR1YWxzIGZyb20gZWFjaCBwb3B1bGF0aW9uIGFuZCBjcm9zcyB0aGVtCkNyZWF0ZSBSSUxzCkluc3BlY3QgZ2VuZXRpYyBhcmNoaXRlY3R1cmUgKyBkbyBRVEwgbWFwcGluZwpXb3JrIGluIFByb2dyZXNzCmBgYHtyfQptZWFuUChwb3BBKQptZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BBKSkpCm1lYW5QKHBvcEIpCm1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcEIpKSkKCgpwbG90VHJhaXRBcmNoaXRlY3R1cmUocG9wQiwgdHdvVHJhaXRGaXRGdW5jKSB8IHBsb3RUcmFpdEFyY2hpdGVjdHVyZShwb3BBLCB0d29UcmFpdEZpdEZ1bmMpCgppbmRBIDwtIHBvcEFbMV0KcGhlbm8oaW5kQSkKaW5kQiA8LSBwb3BCWzFdCnBoZW5vKGluZEIpCgpSSUxfcG9wIDwtIFJhbmRDcm9zcyhpbmRBLCBpbmRCKQpuUklMR2VucyA8LSA4CmZvciAociBpbiAxOm5SSUxHZW5zKSB7CiAgIyBDcmVhdGUgUklMCn0KCiMgRG8gUVRMIG1hcHBpbmcKCmBgYApGaWd1cmUgb3V0IFFUTCBNYXBwaW5nCgo=